package org.jflame.context.lock;

import org.jflame.context.cache.redis.RedisClient;
import org.jflame.context.lock.GlobalLock.LockType;
import org.jflame.context.spring.SpringContextHolder;
import org.jflame.context.spring.aop.BaseMakableAspect;
import org.jflame.context.zookeeper.ZookeeperClient;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;

/**
 * 分布式锁切面.基于spring aop实现,方法执行自动开启分布式锁
 * <p>
 * 注:本地事务应在锁范围内执行,即该切面优先级应大于事务切面
 * 
 * @author charles.zhang
 */
@Aspect
public class GlobalLockAspect extends BaseMakableAspect implements Ordered {

    @Around("@annotation(lockAnnotatation)")
    public Object lockAround(ProceedingJoinPoint joinPoint, GlobalLock lockAnnotatation) throws Throwable {
        Object returnObj = null;
        String lockKey = parseInovkeKey(lockAnnotatation.lockName(), joinPoint);
        DistributedLock lock = null;

        try {
            lock = createLock(lockAnnotatation.lockType(), lockKey, lockAnnotatation.lockTime());
            if (lock.lock(lockAnnotatation.waitTime())) {
                returnObj = joinPoint.proceed();
            } else {
                throw new RuntimeException("访问太频繁,请稍候再试");
            }
        } finally {
            if (lock != null) {
                lock.unlock();
            }
        }

        return returnObj;
    }

    DistributedLock createLock(LockType lockType, String lockKey, Integer lockTime) {
        if (lockType == LockType.redis) {
            RedisClient redisClient = SpringContextHolder.getBean(RedisClient.class);
            return new RedisLock(redisClient, lockKey, lockTime);
        } else if (lockType == LockType.zk) {
            ZookeeperClient zkClient = SpringContextHolder.getBean(ZookeeperClient.class);
            return new ZookeeperLock(zkClient, lockKey, lockTime);
        }
        throw new IllegalStateException(lockType + " not be supported");
    }

    @Override
    public int getOrder() {
        return 0;
    }

}
